package com.zifeng.utils;

import com.alibaba.fastjson.JSON;
import com.zifeng.common.Message;
import com.zifeng.pojo.Block;
import com.zifeng.pojo.Order;
import com.zifeng.pojo.Transaction;
import com.zifeng.service.impl.BlockServiceImpl;
import com.zifeng.common.BlockConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;

/**
 * 共识机制
 * 采用POW即工作量证明实现共识
 */
@Slf4j
@Service
public class PowAlgorithm {

	@Autowired
	private BlockServiceImpl blockServiceImpl;

	@Autowired
	private BlockCache blockCache;
	
	@Autowired
	P2PMessageHandler p2PMessageHandler;
	
	/**
	 * 通过“挖矿”进行工作量证明，实现节点间的共识
	 * 
	 * @return
	 * @throws
	 */
	public Block mine(Order order){
		Block latestBlock = blockCache.getLatestBlock();
		// 封装业务数据集合，记录区块产生的节点信息
		List<Transaction> tsaList = new ArrayList<Transaction>();
		Transaction tsa1 = new Transaction();
		tsa1.setBlockId(latestBlock.getBlockIndex()+1);
		tsa1.setBusinessInfo("订单信息为："+JSON.toJSONString(order));
		tsaList.add(tsa1);
		
		// 定义每次哈希函数的结果 
		String newBlockHash = "";
		int nonce = 0;
		long start = System.currentTimeMillis();
		log.info("开始挖矿");
		while (true) {
			// 计算新区块hash值
			newBlockHash = blockServiceImpl.calculateHash(latestBlock.getBlockHash(), tsaList, nonce);
			// 校验hash值
			if (blockServiceImpl.isValidHash(newBlockHash)) {
				break;
			}
			System.out.println("第"+(nonce+1)+"次尝试计算的hash值：" + newBlockHash);
			nonce++;
		}
		log.info("挖矿完成，正确的hash值：{}", newBlockHash);
		log.info("挖矿耗费时间：{} ms" ,(System.currentTimeMillis() - start));
		// 创建新的区块
		Block block = blockServiceImpl.createNewBlock(nonce, latestBlock.getBlockHash(), newBlockHash, tsaList);
		for (Transaction transaction : tsaList) {
			transaction.setId(0); // 保证在挖矿时计算初的Hash跟节点同步时计算的Hash值相同，否则会同步失败
		}
		block.setTransactions(tsaList);
        //创建成功后，全网广播出去
		Message msg = new Message();
		msg.setType(BlockConstant.RESPONSE_LATEST_BLOCK);
		msg.setData(JSON.toJSONString(block));
		p2PMessageHandler.broatcast(JSON.toJSONString(msg));
		return block;
	}
	
}


